/*
MIT License

Copyright (c) 2013 Thekla, Inc, 2018-2020 Jonathan Young

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#pragma once

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef XATLAS_EXPORT_API
#define XATLAS_EXPORT_API 0
#endif

#ifndef XATLAS_IMPORT_API
#define XATLAS_IMPORT_API 0
#endif

#ifndef XATLAS_API
  #if XATLAS_EXPORT_API
    #ifdef _MSC_VER
      #define XATLAS_API __declspec(dllexport)
    #else
      #define XATLAS_API __attribute__((visibility("default")))
    #endif
  #elif XATLAS_IMPORT_API
    #ifdef _MSC_VER
      #define XATLAS_API __declspec(dllimport)
    #else
      #define XATLAS_API
    #endif
  #else
    #define XATLAS_API
  #endif
#endif

typedef enum XatlasChartType {
  XATLAS_CHART_TYPE_PLANAR    = 0,
  XATLAS_CHART_TYPE_ORTHO     = 1,
  XATLAS_CHART_TYPE_LSCM      = 2,
  XATLAS_CHART_TYPE_PIECEWISE = 3,
  XATLAS_CHART_TYPE_INVALID   = 4,
} XatlasChartType;

typedef struct XatlasChart {
  unsigned *      faceArray;
  unsigned        atlasIndex;
  unsigned        faceCount;
  XatlasChartType type;
  unsigned        material;
} XatlasChart;

typedef struct XatlasVertex {
  int      atlasIndex;
  int      chartIndex;
  float    uv[2];
  unsigned xref;
} XatlasVertex;

typedef struct XatlasMesh {
  XatlasChart *  chartArray;
  unsigned *     indexArray;
  XatlasVertex * vertexArray;
  unsigned       chartCount;
  unsigned       indexCount;
  unsigned       vertexCount;
} XatlasMesh;

#define XatlasImageChartIndexMask   0x1FFFFFFF
#define XatlasImageHasChartIndexBit 0x80000000
#define XatlasImageIsBilinearBit    0x40000000
#define XatlasImageIsPaddingBit     0x20000000

typedef struct XatlasAtlas {
  unsigned *   image;
  XatlasMesh * meshes;
  float *      utilization;
  unsigned     width;
  unsigned     height;
  unsigned     atlasCount;
  unsigned     chartCount;
  unsigned     meshCount;
  float        texelsPerUnit;
} XatlasAtlas;

typedef enum XatlasIndexFormat {
  XATLAS_INDEX_FORMAT_UINT16 = 0,
  XATLAS_INDEX_FORMAT_UINT32 = 1,
} XatlasIndexFormat;

typedef struct XatlasMeshDecl {               // https://github.com/jpcy/xatlas/blob/ec707faeac3b95e6b416076a9509718cce105b6a/source/xatlas/xatlas.h#L109
  const void *          vertexPositionData;
  const void *          vertexNormalData;     // Optional.
  const void *          vertexUvData;         // Optional, the input UVs are provided as a hint to the chart generator.
  const void *          indexData;            // Optional.
  
  const bool *          faceIgnoreData;       // Optional, must be faceCount in length, don't atlas faces set to true, ignored faces still exist in the output meshes, XatlasVertex::uv is set to (0, 0) and XatlasVertex::atlasIndex to -1.
  
  const unsigned *      faceMaterialData;     // Optional, must be faceCount in length, only faces with the same material will be assigned to the same chart.
  
  const unsigned char * faceVertexCount;      // Optional, must be faceCount in length, polygon / n-gon support, faces are assumed to be triangles if this is NULL.
  
  unsigned              vertexCount;
  unsigned              vertexPositionStride;
  unsigned              vertexNormalStride;   // Optional.
  unsigned              vertexUvStride;       // Optional.
  unsigned              indexCount;
  int                   indexOffset;          // Optional, adds this offset to all indices.
  unsigned              faceCount;            // Optional if faceVertexCount is NULL, otherwise assumed to be indexCount / 3.
  XatlasIndexFormat     indexFormat;
  
  float                 epsilon;              // Default value: 1.192092896e-07F, vertex positions within epsilon distance of each other are considered colocal.
} XatlasMeshDecl;

typedef enum XatlasAddMeshError {
  XATLAS_ADD_MESH_ERROR_SUCCESS                   = 0,
  XATLAS_ADD_MESH_ERROR_ERROR                     = 1,
  XATLAS_ADD_MESH_ERROR_INDEX_OUT_OF_RANGE        = 2,
  XATLAS_ADD_MESH_ERROR_INVALID_FACE_VERTEX_COUNT = 3,
  XATLAS_ADD_MESH_ERROR_INVALID_INDEX_COUNT       = 4,
} XatlasAddMeshError;

typedef struct XatlasUvMeshDecl {     // https://github.com/jpcy/xatlas/blob/ec707faeac3b95e6b416076a9509718cce105b6a/source/xatlas/xatlas.h#L156
  const void *      vertexUvData;
  const void *      indexData;        // Optional.
  const unsigned *  faceMaterialData; // Optional, must be indexCount / 3 in length, overlapping UVs should be assigned a different material.
  unsigned          vertexCount;
  unsigned          vertexStride;
  unsigned          indexCount;
  int               indexOffset;      // Optional, adds this offset to all indices.
  XatlasIndexFormat indexFormat;
} XatlasUvMeshDecl;

typedef void (*XatlasFuncParameterize)(const float * positions, float * texcoords, unsigned vertexCount, const unsigned * indices, unsigned indexCount);

typedef struct XatlasChartOptions {             // https://github.com/jpcy/xatlas/blob/ec707faeac3b95e6b416076a9509718cce105b6a/source/xatlas/xatlas.h#L173
  XatlasFuncParameterize paramFunc;
  
  float                  maxChartArea;          // Don't grow charts to be larger than this, 0 means no limit.
  float                  maxBoundaryLength;     // Don't grow charts to have a longer boundary than this, 0 means no limit.
  
  // Weights determine chart growth, higher weights mean higher cost for that metric.
  float                  normalDeviationWeight; // Default value: 2, angle between face and average chart normal.
  float                  roundnessWeight;       // Default value: 0.01
  float                  straightnessWeight;    // Default value: 6
  float                  normalSeamWeight;      // Default value: 4, if > 1000, normal seams are fully respected.
  float                  textureSeamWeight;     // Default value: 0.5
  
  float                  maxCost;               // Default value: 2, if total of all metrics * weights > maxCost, don't grow chart, lower values result in more charts.
  unsigned               maxIterations;         // Default value: 1, number of iterations of the chart growing and seeding phases, higher values result in better charts.
  
  bool                   useInputMeshUvs;       // Default value: false, use XatlasMeshDecl::vertexUvData for charts.
  bool                   fixWinding;            // Default value: false, enforce consistent texture coordinate winding.
} XatlasChartOptions;

typedef struct XatlasPackOptions { // https://github.com/jpcy/xatlas/blob/ec707faeac3b95e6b416076a9509718cce105b6a/source/xatlas/xatlas.h#L197
  unsigned maxChartSize;           // Charts larger than this will be scaled down, 0 means no limit.
  
  unsigned padding;                // Number of pixels to pad charts with.
  
  // Unit to texel scale. e.g. a 1x1 quad with texelsPerUnit of 32 will take up approximately 32x32 texels in the atlas.
  // If 0, an estimated value will be calculated to approximately match the given resolution.
  // If resolution is also 0, the estimated value will approximately match a 1024x1024 atlas.
  float    texelsPerUnit;
  
  // If 0, generate a single atlas with texelsPerUnit determining the final resolution.
  // If not 0, and texelsPerUnit is not 0, generate one or more atlases with that exact resolution.
  // If not 0, and texelsPerUnit is 0, texelsPerUnit is estimated to approximately match the resolution.
  unsigned resolution;
  
  bool     bilinear;               // Default value: true, leave space around charts for texels that would be sampled by bilinear filtering.
  
  bool     blockAlign;             // Default value: false, align charts to 4x4 blocks. Also improves packing speed, since there are fewer possible chart locations to consider.
  
  bool     bruteForce;             // Default value: false, slower, but gives the best result. If false, use random chart placement.
  
  bool     createImage;            // Default value: false, create XatlasAtlas::image.
  
  bool     rotateChartsToAxis;     // Default value: true, rotate charts to the axis of their convex hull.
  
  bool     rotateCharts;           // Default value: true, rotate charts to improve packing.
} XatlasPackOptions;

typedef enum XatlasProgressCategory {
  XATLAS_PROGRESS_CATEGORY_ADD_MESH            = 0,
  XATLAS_PROGRESS_CATEGORY_COMPUTE_CHARTS      = 1,
  XATLAS_PROGRESS_CATEGORY_PACK_CHARTS         = 2,
  XATLAS_PROGRESS_CATEGORY_BUILD_OUTPUT_MESHES = 3,
} XatlasProgressCategory;

typedef bool   (*XatlasFuncProgress) (XatlasProgressCategory category, int progress, void * userData); // May be called from any thread, return false to cancel.
typedef void * (*XatlasFuncRealloc)  (void *, size_t);
typedef void   (*XatlasFuncFree)     (void *);
typedef int    (*XatlasFuncPrint)    (const char *, ...);

XATLAS_API XatlasAtlas *      xatlasCreate                 ();
XATLAS_API void               xatlasDestroy                (XatlasAtlas * atlas);

// Add a mesh to the atlas, XatlasMeshDecl data is copied, so it can be freed after xatlasAddMesh returns.
XATLAS_API XatlasAddMeshError xatlasAddMesh                (XatlasAtlas * atlas, const XatlasMeshDecl * meshDecl, unsigned meshCountHint);

// Wait for xatlasAddMesh async processing to finish, xatlasComputeCharts / xatlasGenerate call this internally.
XATLAS_API void               xatlasAddMeshJoin            (XatlasAtlas * atlas);

XATLAS_API XatlasAddMeshError xatlasAddUvMesh              (XatlasAtlas * atlas, const XatlasUvMeshDecl * decl);

// Call after all xatlasAddMesh calls, can be called multiple times to recompute charts with different options.
XATLAS_API void               xatlasComputeCharts          (XatlasAtlas * atlas, const XatlasChartOptions * chartOptions);

// Call after xatlasComputeCharts, can be called multiple times to re-pack charts with different options.
XATLAS_API void               xatlasPackCharts             (XatlasAtlas * atlas, const XatlasPackOptions * packOptions);

// Equivalent to calling xatlasComputeCharts and xatlasPackCharts in sequence, can be called multiple times to regenerate with different options.
XATLAS_API void               xatlasGenerate               (XatlasAtlas * atlas, const XatlasChartOptions * chartOptions, const XatlasPackOptions * packOptions);

// Progress tracking.
XATLAS_API void               xatlasSetProgressCallback    (XatlasAtlas * atlas, XatlasFuncProgress progressFunc, void * progressUserData);

// Custom memory allocation.
XATLAS_API void               xatlasSetAlloc               (XatlasFuncRealloc reallocFunc, XatlasFuncFree freeFunc);

// Custom print function.
XATLAS_API void               xatlasSetPrint               (XatlasFuncPrint print, bool verbose);

// Helper functions for error messages that convert enums to strings.
XATLAS_API const char *       xatlasAddMeshErrorString     (XatlasAddMeshError error);
XATLAS_API const char *       xatlasProgressCategoryString (XatlasProgressCategory category);

// Helper functions to default initialize structs.
XATLAS_API void               xatlasMeshDeclInit           (XatlasMeshDecl * meshDecl);
XATLAS_API void               xatlasUvMeshDeclInit         (XatlasUvMeshDecl * uvMeshDecl);
XATLAS_API void               xatlasChartOptionsInit       (XatlasChartOptions * chartOptions);
XATLAS_API void               xatlasPackOptionsInit        (XatlasPackOptions * packOptions);

#ifdef __cplusplus
} // extern "C"
#endif
